﻿@{
    ViewBag.Title = "WelCome";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@section css{
    <style>
        body {
            height: 100vh;
            align-items: center;
            justify-content: center;
        }

        .canvas {
            display: block;
            border-radius: 50%;
        }

        .canvas-pattern {
            display: none;
        }

        .align-center {
            margin: 0 auto;
            /*position: fixed;*/
            left: 50%;
            top: 50%;
        }
    </style>
}
<div class="widget widget-body">
    <div class="panel panel-default">
        <canvas class="canvas canvas-pattern js-canvas" width="500" height="500"></canvas>
        <canvas class="canvas js-duplicate align-center" width="500" height="500"></canvas>
    </div>
</div>
@section Script
{
    <script type="text/javascript">
        const canvas = document.querySelector('.js-canvas');
        const ctx = canvas.getContext('2d');

        const canvas2 = document.querySelector('.js-duplicate');
        const ctx2 = canvas2.getContext('2d');

        const dim = Math.min(window.innerWidth, window.innerHeight) * 0.774;
        const w = dim;
        const h = dim;
        const midX = w >> 1;
        const midY = h >> 1;

        const PI = Math.PI;
        const TO_RADIAN = PI / 180;

        const maxDepth = 5;
        const maxSpread = 90 * TO_RADIAN;

        let autoAnimate = true;
        let divergeAt = 0.5;
        let spread = 45 * TO_RADIAN;
        let tick = 0;

        let autoSpeed = 0.004;
        let autoTick = 0;

        canvas.width = canvas2.width = w;
        canvas.height = canvas2.height = h;

        class Branch {
            constructor(position = {
                x: 0,
                y: 0
            }, length = 100, divergeAt = 0.5, angle = 0, depth = 0, spread = 45 * TO_RADIAN) {
                this.position = position;
                this.length = length;
                this.divergeAt = divergeAt;
                this.angle = angle;
                this.depth = depth;

                this.color = '#000';
                this.spread = spread;
                this.branches = [];

                this.grow();
            }

            grow() {
                if (!this.canBranch) {
                    return;
                }

                this.branches = [];

                const nextLength = this.length * 0.75;
                const nextDepth = this.depth + 1;

                this.branches.push(
                    new Branch(
                        this.growPosition,
                        nextLength,
                        this.divergeAt,
                        this.angle + this.spread,
                        nextDepth,
                        this.spread
                    ),
                    new Branch(
                        this.growPosition,
                        nextLength,
                        this.divergeAt,
                        this.angle - this.spread,
                        nextDepth,
                        this.spread
                    )
                );
            }

            update(spread, divergeAt) {
                this.spread = spread;
                this.divergeAt = divergeAt;

                this.grow();
            }

            get growPosition() {
                const dl = this.length * this.divergeAt;

                return {
                    x: this.position.x + (Math.cos(this.angle) * dl),
                    y: this.position.y + (Math.sin(this.angle) * dl),
                };
            }

            get canBranch() {
                return this.depth < maxDepth;
            }
        }

        const rootBranch = new Branch({
            x: midX,
            y: midY
        },
            midY * 0.5,
            divergeAt, -90 * TO_RADIAN,
            0,
            spread
        );

        const drawBranch = (branch, phase) => {
            const h = ~~(200 + (160 * phase));
            const l = 50 + ~~(((branch.depth / (maxDepth + 1))) * 50);

            const endX = branch.length;
            const endY = 0;
            const r = 2;

            ctx.save();

            ctx.strokeStyle = `hsl(${h}, 100%, ${l}%)`;
            ctx.translate(branch.position.x, branch.position.y);
            ctx.rotate(branch.angle);

            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(endX, endY);
            ctx.stroke();
            ctx.closePath();

            ctx.beginPath();
            ctx.fillStyle = `hsl(${h}, 100%, 50%)`;
            ctx.arc(endX, endY, r, 0, PI * 2, false);
            ctx.fill();
            ctx.closePath();

            ctx.restore();

            branch.branches.forEach((b) => {
                drawBranch(b, phase);
            });
        };

        const map = (value, start1, stop1, start2, stop2) => ((value - start1) / (stop1 - start1)) * (stop2 - start2) + start2;

        const clear = () => {
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
            ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
        };

        const loop = () => {
            let phase = rootBranch.spread / maxSpread;

            clear(phase);

            if (autoAnimate) {
                phase = map(Math.sin(autoTick), -1, 1, 0, 1);

                spread = phase * maxSpread;
                divergeAt = map(Math.sin(autoTick), -1, 1, 0, 0.5);

                autoTick += autoSpeed;
            }

            rootBranch.update(spread, divergeAt);

            drawBranch(rootBranch, phase);

            const numSegments = 12;
            const angleInc = PI * 2 / numSegments;
            let angle = tick;

            for (let i = 0; i < numSegments; i++) {
                ctx2.save();
                ctx2.translate(midX, midY);
                ctx2.rotate(angle);
                ctx2.drawImage(canvas, -w / 2, -h / 2);
                ctx2.restore();
                angle += angleInc;
            }

            tick += 0.002;

            requestAnimationFrame(loop);
        };

        const onPointerMove = (e) => {
            if (autoAnimate) {
                return;
            }

            const target = (e.touches && e.touches.length) ? e.touches[0] : e;
            const {
                                                    clientX: x,
                clientY: y
                                                } = target;

            const width = window.innerWidth;
            const height = window.innerHeight;

            spread = (x / width) * maxSpread;
            divergeAt = y / height;
        };

        document.body.addEventListener('mousemove', onPointerMove);
        document.body.addEventListener('touchmove', onPointerMove);

        document.addEventListener('mouseenter', () => {
            autoAnimate = false;
        });

        document.addEventListener('mouseleave', () => {
            autoAnimate = true;
        });

        loop();
    </script>
}
